/** !Core DEPUI functions
This module is the core DEPUI module.  It contains code for:
<ul>
<li>DEPUI startup functions</li>
<li>Handling of events</li>
<li>Setting the theme</li>
<li>Setting the default font</li>
<li>Handling of basic gui objects (base objects, text objects, buttons, 
    windows, window borders and the root window</li>
<li>Finding out what portions of an object are visible and getting the theme 
    to draw them.</li>
</ul>
*/

#include "depui/depui.h"
#include "deds/deds.h"
#include <string.h>

#ifdef MX_PLATFORM_DEGFX
#include <time.h>
#include "degfx/degfx.h"
#endif

#define MXMODULE_VECTOR
#define MXMODULE_UTF8

#ifdef MXDEBUG
static const char *mx__eventname[] = {
	"NOTHING",
	"DESTRUCT",
	"EXPOSE",
	"GEOMETRY",
	"DEFAULTRECT",
	"SELECT",
	"VSCROLL",
	"HSCROLL",
	"SCROLLAREA",
	"LIST_CHANGED",
	"THEME_CHANGE",
	"DIRTY",

	"_INTERACT_BEGIN",
	"POINTER_ENTER",
	"POINTER_LEAVE",
	"POINTER_MOVE",
	"POINTER_PRESS",
	"POINTER_RELEASE",
	"POINTER_CLICK",
	"KEY",
	"KEY_UNUSED",
	"_INTERACT_END",

	"FOCUS_GOT",
	"FOCUS_LOST",

	"GFXSEL_OK",
	"FILESEL_OK",
	"FONTSEL_OK",
	"THEMESEL_OK",

	"LAST"
};
#endif

/* Types for font handling */
struct MX__GUIFONT_ELEM;
MX_DLLIST(struct MX__GUIFONT_ELEM) MX__GUIFONT_LIST;

typedef struct MX__GUIFONT_ELEM {
	MX_REF(MX_GUIFONT) ref;
	MX_DLLIST_NODE(struct MX__GUIFONT_ELEM, MX__GUIFONT_LIST);
} MX__GUIFONT_ELEM;

/* Private static data */
typedef struct MX__STATICS {
	MX_WIN root;
	MX_RECT clip;

	MX_POINTER_INFO pointer;
	MX_OBJ_DATA *pointed;
	MX_OBJ_DATA *pointerhold;

	MX_OBJ_DATA *armed;
	MX_WIN_DATA *active;
	MX_WIN_DATA *popup;

	MX__GUIFONT_LIST loadedfonts;
	MX__GUIFONT_LIST builtinfonts;
	 MX_REF(MX_GUIFONT) defaultfont;

#ifdef MX_PLATFORM_DEGFX
	int framerate;
	int framecount;
	clock_t framenext;
#ifdef MXDEBUG
	 MX_BITFIELD(exposing);
#endif
#endif

	 MX_BITFIELD(running);
} MX__STATICS;

static MX__STATICS mx__;

/* Publically accessable static data */
const MX_EVENTSTACK mx = { (MX_OBJ *) & mx__.root, MX_NOTHING, 0, 0, false };
const MX_THEME *mx__theme;

/* System accessable static data */
struct MX_SYSMENU *mx__sysmenu = 0;

/* Some macros to make the code a bit neater */
#define MX__CURRENT MXOBJ(mx.obj)
#define MX__ROOTOBJ MXOBJ(&mx__.root)
#define MX__ROOTWIN MXWIN(&mx__.root)

void mx__event_default(void)
{
	MXDEBUG_INVARIANT(MX__CURRENT);
	MXDEBUG_INVARIANT(MXOBJ(MX__CURRENT->_win));
	assert(MX__CURRENT->_win);

	if (MX__CURRENT->_win->base.win._handler)
		MX__CURRENT->_win->base.win._handler(MX__CURRENT->_win);
	else
		MX__CURRENT->_class();

	MXDEBUG_INVARIANT(MX__CURRENT);
	MXDEBUG_INVARIANT(MXOBJ(MX__CURRENT->_win));
}

#ifdef MXDEBUG
#   ifdef MX_PLATFORM_DEGFX
typedef MX_VECTOR(MX_STRING) MX__STRINGVEC;

static const char *mx__stackmin = 0;
static const char *mx__stackmax = 0;
static unsigned int mx__eventcount = 0;
static unsigned mx__eventlogsize = 20;
static MX__STRINGVEC mx__eventlog;

static void mx__logevent(void)
{
	int len;
	char temp[1024];
	char *ptr;
	MX_STRING string;

	if (mx.event == MX_EXPOSE)
		return;

	sprintf(temp, "%6i %s %s[%p] %p", mx__eventcount++, mx__eventname[mx.event], MXDEBUG_ATOMNAME_GET(mx.obj), (void *) mx.obj, mx.data);

	len = strlen(temp);
	ptr = mx_malloc((unsigned) len + 2);
	strcpy(ptr, temp);

	mx_string(&string);
	mx_string_set(&string, ptr, len, mx_free);
	mx_vector_append(&mx__eventlog, &string, 1);

	if (mx_vector_size(&mx__eventlog) > mx__eventlogsize) {
		mx_string_free(&mx__eventlog.data[0]);
		mx_vector_remove(&mx__eventlog, 0, 1);
	}
}

static void mx__stackcheck(const char *autoptr)
{
	if (mx__stackmin == 0)
		mx__stackmin = autoptr;
	if (mx__stackmax == 0)
		mx__stackmax = autoptr;
	mx__stackmin = MX_MIN(mx__stackmin, autoptr);
	mx__stackmax = MX_MAX(mx__stackmax, autoptr);
}
#   endif
#endif

unsigned int mx__event(MX_OBJ_DATA * obj, MX_EVENT event, const void *data, unsigned int datalen)
{
	unsigned int ret = false;
	const MX_EVENTSTACK prev = mx;
	MX_LOCK objref;
	MX_LOCK winref;

#ifdef MXDEBUG
#   ifdef MX_PLATFORM_DEGFX
	mx__stackcheck((const char *) &ret);
#   endif
#endif

	MXDEBUG_INVARIANT(obj);
	MXDEBUG_INVARIANT(MXOBJ(obj->_win));

	assert(event);
	assert(event != MX_EXPOSE);

#ifdef MXDEBUG
	if (mx__.exposing)
		assert(0);
#endif

	if (event != MX_DESTRUCT) {
		mx_lock(&winref, obj->_win);
		mx_lock(&objref, obj);

		/* Before we try to send a message, filter out interactive messages
		   when an app has a modal window open */
		if ((obj->_win->base.win._modallock) && (obj != MX__ROOTOBJ)
			&& (event > MX__INTERACT_BEGIN) && (event < MX__INTERACT_END))
			goto done;
	}

	/* A (little) dirty hack to cast away the const-ness of the top of the
	   event stack. This allows users to see a const event stack which helps
	   avoid users changing its values.  The ugly cast is only internal to
	   the event handling code so it actually makes users code cleaner and
	   safer at the expese of the ugly cast here and a few other places. */
	((MX_EVENTSTACK *) (&mx))->obj = (MX_OBJ *) obj;
	((MX_EVENTSTACK *) (&mx))->event = event;
	((MX_EVENTSTACK *) (&mx))->data = data;
	((MX_EVENTSTACK *) (&mx))->datalen = datalen;
	((MX_EVENTSTACK *) (&mx))->_answer = 0;

	assert(MX__CURRENT == obj);

#ifdef MXDEBUG
	fprintf(stderr, "Enter event %s to %s[%p] with %p\n", mx__eventname[event], MXDEBUG_ATOMNAME_GET(obj), (void *) obj, data);
#   ifdef MX_PLATFORM_DEGFX
	mx__logevent();
#   endif
#endif

	/* Let the theme wrap the event, or send it directly */
	if (mx__theme->event)
		mx__theme->event();
	else
		mx__event_default();

#ifdef MXDEBUG
	fprintf(stderr, "Exit event %s to %s[%p] with %p\n", mx__eventname[mx.event], MXDEBUG_ATOMNAME_GET(obj), (void *) obj, mx.data);
#endif

	ret = mx._answer;

	/* Ugly cast so that user can see a const event stack. */
	memcpy((MX_EVENTSTACK *) & mx, &prev, sizeof(prev));

  done:
	if (event != MX_DESTRUCT) {
		if (mx_lastlock(&objref))
			mx__dirty(obj, true);
		if (mx_lastlock(&winref))
			mx_win_dirty(obj->_win);
		mx_unlock(&objref);
		mx_unlock(&winref);
	}

	return ret;
}

/**events !Emit an event to parent object
This function informs the current objects parent of an event.  This is
indended for very local communication.  This is used between scrollbars (MX_SLIDER) 
and scrollabale areas (MX_SCROLL) to indicate movement of the scrollbar. */
unsigned int mx_emit(MX_EVENT event, const void *data, unsigned int datalen)
{
	if (!mx_tree_parent(MX__CURRENT))
		return 0;

	return mx__event(mx_tree_parent(MX__CURRENT), event, data, datalen);
}

/**events !Inform parent window of event
This function passes an event to a parent window of the object/window
currently handling an event.  This is intended for child windows to pass
events to thier parents without explicitly knowing who thier parent are. */
unsigned int mx_inform(MX_EVENT event, const void *data, unsigned int datalen)
{
	assert(MX__CURRENT->_win);

	if (mx_tree_parent(MXWIN(MX__CURRENT->_win)))
		return mx_event(mx_tree_parent(MXWIN(MX__CURRENT->_win)), event, data, datalen);

	return 0;
}

/**events !Set the answer to an event
This function sets the return value of the event. */
void mx_answer(unsigned int answer)
{
				/** This function does not force an end to event handling, you have to do that
        by returning from the event handler. */

				/** If this function is called twice during the handling of the event then
        the first return value is forgotten and the last one is used. */

	/* Again the ugly cast so that users see a const event stack. */
	((MX_EVENTSTACK *) (&mx))->_answer = answer;
}

static void mx__theme_inform(MX_OBJ_DATA * obj)
{
	MX_OBJ_DATA *ptr = mx_tree_last(obj);

	while (ptr) {
		mx__theme_inform(ptr);
		ptr = mx_tree_prev(ptr);
	}

	mx__event(obj, MX_THEME_CHANGE, 0, 0);
}

/**themes !Set the current theme
This function changes the current theme. */
void mx_theme_set(const MX_THEME * theme)
{
	const MX_THEME *old = mx__theme;

				/** This function does nothing if the requested theme is already being used. */
	if (theme == mx__theme)
		return;

				/** The current theme is stopped before trying the new one. */
	old->stop();

	/* Try to start the new theme */
	if ((theme) && (theme->start())) {
		mx__theme = theme;

				/** If the new theme cannot be started the previous theme is restarted. */
	} else if ((old) && (old->start())) {

				/** If both the new theme and the previous theme can't be started the
    default theme for the platform is found and started. */
	} else {
		int i = 0;
		const MX_PLATFORM_RESOLUTION *r = 0;
		const MX_PLATFORM_DEPTH *c = 0;
		const MX_PLATFORM_DRIVER *d = 0;
		const MX_PLATFORM_THEME *t = 0;

		/* Ask about all themes */
		mx_platform_modes(&d, &r, &c, &t);

		/* Try starting the themes in order */
		if (t) {
			do {
				mx__theme = &t->theme[i++];
			}
			while ((mx__theme) && (!mx__theme->start()));
		}

								/**todo If we get here then NO themes, not even the default, work
        anymore.  Indicate some kind of (platform specific?) fatal error. */
		assert(mx__theme);
	}

	assert(mx__theme);

				/** When the new theme is started successfully, all objects in the
        main tree get an MX_THEME_CHANGE event so that they can change their
        sizes or geometry to handle the new theme properly. */
	if (old != mx__theme) {
		mx__theme_inform(MX__ROOTOBJ);
		mx_platform_dirty(mx_platform_rect());
	}
}

static void mx__guifont_entry(MX_GUIFONT * guifont, MX__GUIFONT_LIST * list)
{
	MX__GUIFONT_ELEM *entry = mx_malloc(sizeof(MX__GUIFONT_ELEM));

	assert(entry);
	assert(guifont);
	assert(list);

	MXDEBUG_ALLOCTAG(entry);

	memset(entry, 0, sizeof(*entry));

	mx_ref(&entry->ref, guifont);
	mx_delete(guifont);

	MX_DLLIST_APPEND(entry, list);
}

static void mx__guifont_destruct(void *atom)
{
	MX_GUIFONT *guifont = (MX_GUIFONT *) atom;

	assert(guifont);

	if (guifont->allocated) {
		assert(guifont->_font.font);
		mx_platform_font_free(guifont->_font.font);
	}

	/* Even system fonts had a description line allocated, so delete it */
	if (guifont->_font.text)
		mx_free((void *) guifont->_font.text);
}

static MX_GUIFONT *mx__guifont(void *font, const char *text, unsigned allocated)
{
	char *ptr;
	MX_GUIFONT *guifont;

	if (text == 0)
		text = "";

	guifont = mx__atom(0, mx__guifont_destruct, sizeof(MX_GUIFONT));
	assert(guifont);
	MXDEBUG_ALLOCTAG(guifont);

	guifont->_font.font = font;
	guifont->allocated = allocated;

	ptr = mx_malloc(strlen(text) + 1);
	assert(ptr);
	strcpy(ptr, text);
	guifont->_font.text = ptr;

	MXDEBUG_ATOMNAME(guifont, ptr);

	return guifont;
}

/**guifonts !Load a gui font from a file
The request is passed on to the platform code and the DEPUI core code
keeps track of what fonts are in use and what fonts need deleting.  */
MX_GUIFONT *mx_guifont_load(const char *filename, const char *text)
{
	MX_GUIFONT *guifont;
	void *platform_font = 0;

	if (text == 0)
		text = "";

	if (filename)
		platform_font = mx_platform_font_load(filename);

	if (!platform_font)
		return 0;

	guifont = mx__guifont(platform_font, text, true);
	MXDEBUG_ALLOCTAG(guifont);

	mx__guifont_entry(guifont, &mx__.loadedfonts);

	return guifont;
}

/**guifonts !Returns the available gui fonts. */
MX_GUIFONT *mx_guifont_index(int i)
{
	int j = 0;
	MX__GUIFONT_ELEM *entry;

				/** Pass a number less than 0 and the default font will be returned. */
	if (i < 0)
		return MXREF(mx__.defaultfont);

				/** Lower indicies return platform builtin fonts. */
	entry = mx_dllist_first(&mx__.builtinfonts);
	while (entry) {
		if (i == j)
			return MXREF(entry->ref);
		++j;
		entry = mx_dllist_next(entry);
	}

				/** Higher indicies return platform loaded fonts. */
	entry = mx_dllist_first(&mx__.loadedfonts);
	while (entry) {
		if (i == j)
			return MXREF(entry->ref);
		++j;
		entry = mx_dllist_next(entry);
	}

				/** Too high indicies returns 0. */
	return 0;
}

/**guifonts !Get font desriptive text
This function return a portion of descriptive text for a font put there
by the platform code. */
const char *mx_guifont_text(const MX_GUIFONT * guifont)
{
	assert(guifont);

	return guifont->_font.text;
}

/**guifonts !Set the default gui font */
void mx_guifont_default(MX_GUIFONT * guifont)
{
	if (guifont == MXREF(mx__.defaultfont))
		return;

	mx_unref(&mx__.defaultfont);

	if (guifont) {
		mx_ref(&mx__.defaultfont, guifont);
		mx_delete(guifont);
	}

	mx__theme_inform(MX__ROOTOBJ);
	mx_platform_dirty(mx_platform_rect());
}

/**pointer !Returns pointer information */
const MX_POINTER_INFO *mx_pointer_info(void)
{
				/** It can be called at any time. */
	return &mx__.pointer;
}

/**pointer !Hold the pointer
If the object handling the current event is pointed to by the pointer 
then the object will keep recieving pointer messages even if the pointer 
leaves the object.  This can be 'turned off' by calling <code>mx_pointer_release()
</code>. */
unsigned mx_pointer_hold(void)
{
/** If the current object does not have the pointer then this function does nothing. */
	if (MXOBJ(mx.obj) == mx__.pointed) {
		mx__.pointerhold = mx__.pointed;

								/** This function return non-zero if the pointer has been successfully held. */
		return true;
	}
	return false;
}

/**pointer !Release the pointer
If the object handling the current event has the pointer held using
<code>mx_pointer_hold()</code> then this function 'releases' the pointer. */
unsigned mx_pointer_release(void)
{
/** If the current object is not holding the pointer then this function does nothing. */
	if (MXOBJ(mx.obj) == mx__.pointerhold) {
		mx__.pointerhold = 0;

								/** This function return non-zero if the pointer has been successfully released. */
		return true;
	}
	return false;
}

void mx__wantmove(MX_OBJ_DATA * obj, unsigned wantmove)
{
	MXDEBUG_INVARIANT(obj);

	obj->_wantmove = (wantmove) ? true : false;
}

void mx__wantpointer(MX_OBJ_DATA * obj, unsigned wantpointer)
{
	MXDEBUG_INVARIANT(obj);

	obj->_wantpointer = (wantpointer) ? true : false;
}

unsigned mx__haspointer(MX_OBJ_DATA * obj)
{
	MXDEBUG_INVARIANT(obj);

	if (mx__.pointerhold) {
		if (obj == mx__.pointerhold)
			return true;
	} else if (obj == mx__.pointed)
		return true;

	return false;
}

/** !Return keypress information. */
const MX_KEY_INFO *mx_key_info(void)
{
	assert(mx.data);
	assert((mx.event == MX_KEY) || (mx.event == MX_KEY_UNUSED));

/** This function should only be called when handling a MX_KEY or MX_KEY_UNUSED event. */
	if ((mx.event == MX_KEY) || (mx.event == MX_KEY_UNUSED))
		return (const MX_KEY_INFO *) mx.data;

	return 0;
}

int mx__x(const MX_OBJ_DATA * obj)
{
	MXDEBUG_INVARIANT(obj);

	if (mx_tree_parent(obj))
		return mx_x1(obj) - mx_x1(mx_tree_parent(obj));

	return mx_x1(obj);
}

int mx__y(const MX_OBJ_DATA * obj)
{
	MXDEBUG_INVARIANT(obj);

	if (mx_tree_parent(obj))
		return mx_y1(obj) - mx_y1(mx_tree_parent(obj));

	return mx_y1(obj);
}

static void mx__place_move(MX_OBJ_DATA * obj, const int dx, const int dy)
{
	MX_RECT rect;

	rect = *MXRECT(obj);

	rect.x1 -= dx;
	rect.y1 -= dy;
	rect.x2 -= dx;
	rect.y2 -= dy;
	mx_rectatom_place(obj, &rect);

	obj = mx_tree_last(obj);
	while (obj) {
		mx__place_move(obj, dx, dy);
		obj = mx_tree_prev(obj);
	}
}

void mx__place(MX_OBJ_DATA * obj, const MX_RECT * rect)
{
	MX_OBJ_DATA *ptr;
	int dx, dy;

	ptr = mx_tree_last(obj);
	dx = mx_x1(obj);
	dy = mx_y1(obj);

	assert(rect);
	MXDEBUG_INVARIANT(obj);

	mx_rectatom_place(obj, rect);

	dx -= mx_x1(obj);
	dy -= mx_y1(obj);

	while (ptr) {
		mx__place_move(ptr, dx, dy);
		ptr = mx_tree_prev(ptr);
	}
}

void mx__position(MX_OBJ_DATA * obj, int x, int y, int w, int h)
{
	MX_RECT rect;

	MXDEBUG_INVARIANT(obj);
	MXDEBUG_INVARIANT(mx_tree_parent(obj));
	assert(obj != MX__ROOTOBJ);

	rect = *MXRECT(obj);
	if (x != MXDEFAULT)
		rect.x1 = x + mx_x1(mx_tree_parent(obj));
	if (y != MXDEFAULT)
		rect.y1 = y + mx_y1(mx_tree_parent(obj));
	if (w == MXDEFAULT)
		w = mx_w(obj);
	if (h == MXDEFAULT)
		h = mx_h(obj);
	rect.x2 = rect.x1 + w;
	rect.y2 = rect.y1 + h;

	mx__place(obj, &rect);
}

void mx__move(MX_OBJ_DATA * obj, int x, int y)
{
	MXDEBUG_INVARIANT(obj);

	mx__position(obj, x, y, mx_w(obj), mx_h(obj));
}

void mx__resize(MX_OBJ_DATA * obj, int w, int h)
{
	MXDEBUG_INVARIANT(obj);

	mx__position(obj, mx__x(obj), mx__y(obj), w, h);
}

void mx__geometry(MX_OBJ_DATA * obj)
{
	MXDEBUG_INVARIANT(obj);

	mx__event(obj, MX_GEOMETRY, 0, 0);
}

void mx__defaultrect(MX_OBJ_DATA * obj, MX_RECT * rect)
{
	MX_RECT data;

	MXDEBUG_INVARIANT(obj);
	assert(obj != MX__ROOTOBJ);

	data = *MXRECT(obj);

	mx__event(obj, MX_DEFAULTRECT, &data, sizeof(data));

	if (rect)
		*rect = data;
	else
		mx__place(obj, &data);
}

/** During handling the MX_DEFAULTRECT event an object should call this
function to indicate its desired area. */
MX_RECT *mx_defaultrect_data(void)
{
	assert(mx.data);
	assert(mx.event == MX_DEFAULTRECT);

	MXDEBUG_INVARIANT(MX__CURRENT);

/** This function should only be called while handling an MX_DEFAULTRECT event. */
	if (mx.event == MX_DEFAULTRECT)
		return (MX_RECT *) mx.data;

	return 0;
}

void mx__layout(MX_OBJ_DATA * obj, MX_LAYOUT flags, const MX_OBJ_DATA * other, int x, int y)
{
	int w, h;
	MX_RECT rect, objrect;

	if (!other)
		other = MX__ROOTOBJ;

	MXDEBUG_INVARIANT(obj);
	MXDEBUG_INVARIANT(other);
	assert(obj != MX__ROOTOBJ);

	objrect = *MXRECT(obj);
	rect = *MXRECT(other);

	w = objrect.x2 - objrect.x1;
	h = objrect.y2 - objrect.y1;

	if (flags & MX_LAYOUT_W)
		w = rect.x2 - rect.x1;

	if (flags & MX_LAYOUT_H)
		h = rect.y2 - rect.y1;

	if (flags & MX_LAYOUT_LEFT)
		objrect.x1 = rect.x1 - w - x;

	else if (flags & MX_LAYOUT_RIGHT)
		objrect.x1 = rect.x2 + x;

	else if (flags & MX_LAYOUT_X1)
		objrect.x1 = rect.x1 + x;

	else if (flags & MX_LAYOUT_X2)
		objrect.x1 = rect.x2 - w + x;

	else if (flags & MX_LAYOUT_HCENTER)
		objrect.x1 = (rect.x2 - rect.x1 - w) / 2 + x;

	if (flags & MX_LAYOUT_TOP)
		objrect.y1 = rect.y1 - h - y;

	else if (flags & MX_LAYOUT_BOTTOM)
		objrect.y1 = rect.y2 + y;

	else if (flags & MX_LAYOUT_Y1)
		objrect.y1 = rect.y1 + y;

	else if (flags & MX_LAYOUT_Y2)
		objrect.y1 = rect.y2 - h + y;

	else if (flags & MX_LAYOUT_VCENTER)
		objrect.y1 = (rect.y2 - rect.y1 - h) / 2 + y;

	objrect.x2 = objrect.x1 + w;
	objrect.y2 = objrect.y1 + h;

	mx__place(obj, &objrect);
}

unsigned mx__selected(const MX_OBJ_DATA * obj)
{
	MXDEBUG_INVARIANT(obj);

	return (obj->_selected) ? true : false;
}

unsigned int mx__select(MX_OBJ_DATA * obj, unsigned doselect)
{
	MXDEBUG_INVARIANT(obj);

	if (obj->_disabled)
		return 0;

	if ((obj->_selected) && (doselect))
		return 0;

	if ((!obj->_selected) && (!doselect))
		return 0;

	return mx__event(obj, MX_SELECT, (doselect) ? (obj) : 0, sizeof(obj));
}

void mx__disable(MX_OBJ_DATA * obj, unsigned disable)
{
	MXDEBUG_INVARIANT(obj);

	obj->_disabled = (disable) ? true : false;
}

unsigned mx__disabled(const MX_OBJ_DATA * obj)
{
	MXDEBUG_INVARIANT(obj);

	if (obj->_win->base.win._modallock)
		return true;

	return (obj->_disabled) ? true : false;
}

unsigned mx__wantfocus(MX_OBJ_DATA * obj, unsigned wantfocus)
{
	unsigned int ret = false;
	const unsigned hadfocus = obj->_wantfocus;

	MXDEBUG_INVARIANT(obj);

	obj->_wantfocus = (wantfocus) ? true : false;

	/* Object can lose the focus */
	if ((!hadfocus) && (obj == obj->_win->base.win._focus)) {
		obj->_win->base.win._focus = 0;
		ret = true;
	}

	/* Object can gain the focus */
	if ((hadfocus) && (!obj->_win->base.win._focus)) {
		obj->_win->base.win._focus = obj;
		ret = true;
	}
	return ret;
}

static void mx__focus(MX_OBJ_DATA * obj)
{
	MXDEBUG_INVARIANT(obj);
	MXDEBUG_INVARIANT(MXOBJ(obj->_win));

	if (!obj->_wantfocus)
		return;

	if (obj == obj->_win->base.win._focus)
		return;

	if (obj->_disabled)
		return;

	if (obj->_win->base.win._focus) {
		mx__dirty(obj->_win->base.win._focus, true);
		mx__event(obj->_win->base.win._focus, MX_FOCUS_LOST, 0, 0);
	}

	obj->_win->base.win._focus = obj;
	mx__dirty(obj, true);
	mx__event(obj, MX_FOCUS_GOT, 0, 0);
}

unsigned mx__focused(const MX_OBJ_DATA * obj)
{
	MXDEBUG_INVARIANT(obj);

	if ((mx__.active) && (mx__.active == MXWIN(obj->_win))
		&& (mx__.active->_focus == obj))
		return true;

	return false;
}

unsigned mx__focus_default(MX_OBJ_DATA * obj)
{
	MXDEBUG_INVARIANT(obj);
	MXDEBUG_INVARIANT(MXOBJ(obj->_win));

	if (!obj->_wantfocus)
		return false;

	if (obj == obj->_win->base.win._defaultfocus)
		return true;

	obj->_win->base.win._defaultfocus = obj;
	return true;
}

unsigned mx__active(const MX_WIN_DATA * win)
{
	MXDEBUG_INVARIANT(MXOBJ(win));

	if ((mx__.active) && (mx__.active == win))
		return true;

	return false;
}

unsigned mx__armed(const MX_OBJ_DATA * obj)
{
	MXDEBUG_INVARIANT(obj);

	if ((mx__.armed) && (mx__.armed == obj))
		return true;

	return false;
}

void mx_obj_class(void)
{
	MX_OBJ_DATA *obj = MX__CURRENT;

	MXDEBUG_CLASSINVARIANT(obj);

	switch (mx.event) {

	case MX_DESTRUCT:
		while (mx_tree_last(obj)) {
			MX_OBJ_DATA *ptr = mx_tree_last(obj);

			MX_TREE_REMOVE(ptr);
			mx_delete(ptr);
		}
		return;

	case MX_EXPOSE:
		mx__theme->obj(mx.obj);
		return;

	case MX_DIRTY:
		mx__dirty(obj, true);
		return;

	case MX_THEME_CHANGE:
		mx__geometry(obj);
		return;

	case MX_SELECT:
		obj->_selected = (mx.data) ? true : false;
		return;

	default:
		return;
	}
}

static void mx__destroy(void *atom)
{
	MX_OBJ_DATA *obj = (MX_OBJ_DATA *) atom;

	MXDEBUG_INVARIANT(obj);

#ifdef MXDEBUG
	fprintf(stderr, "@@ Destroy %s[%p]\n", MXDEBUG_ATOMNAME_GET(obj), (void *) obj);
	assert(obj->_destroying == false);
	assert(mx__.exposing == false);
	obj->_destroying = true;
#endif

/**fix Should drawing be done in destructor?
        if (mx_tree_parent(obj))
                mx_platform_dirty(MXRECT(obj)); */

	mx__event(obj, MX_DESTRUCT, 0, 0);

	if ((obj->_win) && (obj == obj->_win->base.win._focus))
		obj->_win->base.win._focus = 0;

	if ((obj->_win) && (obj == obj->_win->base.win._defaultfocus))
		obj->_win->base.win._defaultfocus = 0;

	MX_TREE_REMOVE(obj);

	if (obj == mx__.pointed) {
		mx__.pointed = 0;
		mx__.pointer.x = -1;
	}

	if (obj == mx__.pointerhold) {
		mx__.pointerhold = 0;
		mx__.pointer.x = -1;
	}

	if (obj == mx__.armed)
		mx__.armed = 0;

	if (obj == MXOBJ(mx__.active))
		mx__.active = 0;

	if (obj == MXOBJ(mx__.popup))
		mx__.active = 0;
}

void *mx__obj(MX_OBJ_DATA * obj, MX_CLASS _class, size_t size, MX_OBJ_DATA * parent, int theid)
{
	MX_RECT rect;

	if (obj != MX__ROOTOBJ)
		if (!mx_start())
			return 0;

	if (!parent)
		parent = MX__ROOTOBJ;

	if (obj != MX__ROOTOBJ) {
		MXDEBUG_INVARIANT(parent);
		assert(obj != parent);
	}

	MXMINSIZE(size, MX_OBJ);
	obj = (MX_OBJ_DATA *) mx_rectatom(obj, mx__destroy, size);
	assert(obj);
	MXDEBUG_ALLOCTAG(obj);

	obj->_class = (_class) ? (_class) : mx_obj_class;
	obj->_win = (obj == MXOBJ(&mx__.root)) ? (&mx__.root) : (parent->_win);
	obj->_selected = false;
	obj->_id = theid;
	MXDEBUG_ATOMNAME(obj, "obj");
#ifdef MXDEBUG
	obj->_magic = MXMAGIC;
#endif

	rect.x1 = -1;
	rect.y1 = -1;
	rect.x2 = -1;
	rect.y2 = -1;
	mx_rectatom_place(obj, &rect);

	if (obj != parent)
		MX_TREE_INSERT(obj, parent);

	MXDEBUG_INVARIANT(obj);
	return obj;
}

void *mx__textual_platform_font(const MX_TEXTUAL_DATA * textual)
{
	MXDEBUG_INVARIANT(MXOBJ(textual));

	if (MXREF(textual->_font))
		return MXREF(textual->_font)->_font.font;

	if (MXREF(mx__.defaultfont))
		return MXREF(mx__.defaultfont)->_font.font;

	return 0;
}

MX__TEXTCHUNK *mx__textchunk(MX_TEXTUAL_DATA * textual)
{
	MX__TEXTCHUNK *chunk = 0;

	/* If no chunks in list, use the builtin one. */
	if (mx_dllist_first(textual) == 0)
		chunk = &textual->_first;
	else
		chunk = mx_malloc(sizeof(MX__TEXTCHUNK));

	MXDEBUG_INVARIANT(MXOBJ(textual));
	assert(chunk);

	memset(chunk, 0, sizeof(*chunk));
	mx_string(&chunk->text);

	MX_DLLIST_APPEND(chunk, textual);

	return chunk;
}

void mx__textchunk_free(MX__TEXTCHUNK * chunk)
{
	const MX_TEXTUAL_DATA *textual = mx_dllist_list(chunk);

	MXDEBUG_INVARIANT(MXOBJ(textual));

	mx_string_free(&chunk->text);

	MX_DLLIST_REMOVE(chunk);

	/* Dont free the builtin chunk */
	if (chunk != &textual->_first)
		mx_free(chunk);
}

void mx___textual_chunker(MX_TEXTUAL_DATA * textual)
{
	int lineheight;
	int endn = 0, len = 0, chunklen = 0;
	const char *text = mx__textual_text(textual, &len);
	const char *end = text, *begin = text;
	void *font = mx__textual_platform_font(textual);
	const int spacew = mx_platform_font_width(font, " ", -1);

	MX__TEXTCHUNK *chunk = mx_dllist_first(textual);

	if (!chunk)
		chunk = mx__textchunk(textual);

	assert(chunk);
	assert(chunk == &textual->_first);
	MXDEBUG_INVARIANT(MXOBJ(textual));

	lineheight = mx_platform_font_height(font);
	textual->_textwidth = 0;
	textual->_textheight = lineheight;

	if (len < 0)
		len = 0x7fff;

	mx_string_set(&chunk->text, begin, chunklen, 0);
	chunk->offset = 0;
	chunk->rect.x1 = 0;
	chunk->rect.y1 = 0;
	chunk->rect.x2 = 0;
	chunk->rect.y2 = lineheight;
	chunk->normal = true;
	chunk->cursor = false;

	while ((end) && (*end) && (endn < len)) {
		int pixw;
		MX__TEXTCHUNK *prev = chunk;
		const int clen = mx_utf8_len(end);

		/* Handle spacing for whitespace */
		if ((*end == '\n') || (*end == ' ') || (*end == '\t'))
			pixw = spacew;
		else
			pixw = mx_platform_font_width(font, end, clen);

		/* Break chunk at carriage return */
		if (*end == '\n') {

			/* Start a new chunk after the carriage return */
			prev = chunk;
			chunk = mx_dllist_next(chunk);
			if (!chunk)
				chunk = mx__textchunk(textual);

			begin = end + clen;
			chunklen = 0;
			mx_string_set(&chunk->text, begin, chunklen, 0);
			chunk->offset = endn + clen;
			chunk->rect.x1 = 0;
			chunk->rect.y1 = prev->rect.y1 + lineheight;
			chunk->rect.x2 = 0;
			chunk->rect.y2 = prev->rect.y2 + lineheight;
			chunk->normal = true;
			chunk->cursor = false;

			/* Break chunk at tab and put in chunk for the tab */
		} else if (*end == '\t') {
			const int tabsize = spacew * textual->_tabsize;
			const int tabwidth = tabsize - (prev->rect.x2 + 1) % tabsize;

			/* Start a new chunk for the tab */
			prev = chunk;
			chunk = mx_dllist_next(chunk);
			if (!chunk)
				chunk = mx__textchunk(textual);

			mx_string_set(&chunk->text, 0, -1, 0);
			chunk->offset = endn + clen;
			chunk->rect.x1 = prev->rect.x2 + 1;
			chunk->rect.y1 = prev->rect.y1;
			chunk->rect.x2 = chunk->rect.x1 + tabwidth;
			chunk->rect.y2 = prev->rect.y2;
			chunk->normal = true;
			chunk->cursor = false;

			/* Start a new chunk after the tab */
			prev = chunk;
			chunk = mx_dllist_next(chunk);
			if (!chunk)
				chunk = mx__textchunk(textual);

			begin = end + clen;
			chunklen = 0;
			mx_string_set(&chunk->text, begin, chunklen, 0);
			chunk->offset = endn + clen;
			chunk->rect.x1 = prev->rect.x2 + 1;
			chunk->rect.y1 = prev->rect.y1;
			chunk->rect.x2 = chunk->rect.x1;
			chunk->rect.y2 = prev->rect.y2;
			chunk->normal = true;
			chunk->cursor = false;

			/* Accept character into current chunk */
		} else {
			chunklen += clen;
			chunk->rect.x2 += pixw;
			textual->_textwidth = MX_MAX(textual->_textwidth, chunk->rect.x2);
			mx_string_set(&chunk->text, begin, chunklen, 0);
		}

		textual->_textheight = chunk->rect.y2;
		end += clen;
		endn += clen;
	}

	/* Remove excess chunks */
	while (chunk != mx_dllist_last(textual))
		mx__textchunk_free(mx_dllist_last(textual));
}

void mx__textual_chunk(MX_TEXTUAL_DATA * textual)
{
	if (textual->_chunker)
		textual->_chunker(textual);
	else
		mx___textual_chunker(textual);
}

static void mx__textual_setter(MX_TEXTUAL_DATA * textual, const char *str, int len, MX_FREE dfree)
{
	MXDEBUG_INVARIANT(MXOBJ(textual));

	mx_string_set(&textual->_text, str, len, dfree);

	mx__textual_chunk(textual);
}

static const char *mx__textual_getter(const MX_TEXTUAL_DATA * textual, int *len)
{
	MXDEBUG_INVARIANT(MXOBJ(textual));

	return mx_string_text(&textual->_text, len);
}

void mx__textual_set(MX_TEXTUAL_DATA * textual, const char *str, int len, MX_FREE dfree)
{
	MXDEBUG_INVARIANT(MXOBJ(textual));

	if (textual->_setter)
		textual->_setter(textual, str, len, dfree);
	else
		mx__textual_setter(textual, str, len, dfree);
}

void mx__textual_setcopy(MX_TEXTUAL_DATA * textual, const char *str, int len)
{
	char *text;

	MXDEBUG_INVARIANT(MXOBJ(textual));

	if (len < 0)
		len = strlen(str);

	text = mx_malloc((unsigned) len + 1);
	MXDEBUG_ALLOCTAG(text);

	assert(text);
	memcpy(text, str, (unsigned) len);
	text[len] = '\0';

	mx__textual_set(textual, text, len, mx_free);
}

const char *mx__textual_text(const MX_TEXTUAL_DATA * textual, int *len)
{
	MXDEBUG_INVARIANT(MXOBJ(textual));

	if (textual->_getter)
		return textual->_getter(textual, len);
	else
		return mx__textual_getter(textual, len);
}

void mx__textual_font(MX_TEXTUAL_DATA * textual, MX_GUIFONT * font)
{
	MXDEBUG_INVARIANT(MXOBJ(textual));

	if (font == MXREF(textual->_font))
		return;

	mx_unref(&textual->_font);

	mx_ref(&textual->_font, font);
}

void mx__textual_align(MX_TEXTUAL_DATA * textual, MX_ALIGN align)
{
	MXDEBUG_INVARIANT(MXOBJ(textual));
	textual->_align = align;
}

MX_ALIGN mx__textual_align_get(const MX_TEXTUAL_DATA * textual)
{
	MXDEBUG_INVARIANT(MXOBJ(textual));
	return textual->_align;
}

void mx_textual_class(void)
{
	MX_TEXTUAL *t = (MX_TEXTUAL *) mx.obj;
	MX_TEXTUAL_DATA *textual = MXTEXTUAL(t);

	MXDEBUG_CLASSINVARIANT(MXOBJ(t));

	switch (mx.event) {

	case MX_DESTRUCT:
		while (mx_dllist_last(textual))
			mx__textchunk_free(mx_dllist_last(textual));

		mx_unref(&textual->_font);
		mx_string_free(&textual->_text);
		break;

	case MX_EXPOSE:
		mx__theme->textual(t);
		return;

	case MX_DEFAULTRECT:
		{
			MX_RECT *rect = mx_defaultrect_data();

			rect->x2 = rect->x1 + textual->_textwidth;
			rect->y2 = rect->y1 + textual->_textheight;
			return;
		}

	case MX_GEOMETRY:
	case MX_THEME_CHANGE:
		mx__textual_chunk(textual);
		break;

	default:
		break;
	}
	mx_obj_class();
}

MX_TEXTUAL *mx__textual(MX_TEXTUAL_DATA * textual, size_t size, MX_OBJ_DATA * parent, int theid)
{
	MXMINSIZE(size, MX_TEXTUAL);

	textual = (MX_TEXTUAL_DATA *) mx__obj(MXOBJ(textual), mx_textual_class, size, parent, theid);
	if (textual) {
		MXDEBUG_ALLOCTAG(textual);
		MXDEBUG_ATOMNAME(textual, "textual");

		mx_string(&textual->_text);
		mx__textual_align(textual, MX_ALIGN_NONE);

		textual->_tabsize = 4;
		mx__textual_set(textual, 0, -1, 0);
	}
	MXDEBUG_INVARIANT(MXOBJ(textual));
	return (MX_TEXTUAL *) textual;
}

void mx_button_class(void)
{
	MX_BUTTON *button = (MX_BUTTON *) mx.obj;
	MX_TEXTUAL_DATA *textual = MXTEXTUAL(button);

	MXDEBUG_CLASSINVARIANT(MXOBJ(button));

	switch (mx.event) {

	case MX_EXPOSE:
		mx__theme->button(button);
		return;

	case MX_POINTER_PRESS:
		mx_dirty(button, true);
		break;

	case MX_POINTER_RELEASE:
	case MX_POINTER_LEAVE:
		if (mx_armed(button))
			mx_dirty(button, true);
		break;

	case MX_KEY:
		{
			const MX_KEY_INFO *info = mx_key_info();

			if ((info) && ((info->ascii == ' ') || (info->ascii == '\r'))) {
				mx_select(button, !mx_selected(button));
				mx_dirty(button, true);
				mx_answer(true);
			}
			return;
		}

	case MX_POINTER_CLICK:
		mx_select(button, !mx_selected(button));
		mx_dirty(button, true);
		break;

	case MX_DEFAULTRECT:
		{
			MX_RECT *rect = mx_defaultrect_data();

			rect->x2 = rect->x1 + textual->_textwidth + 8;
			rect->y2 = rect->y1 + textual->_textheight + 4;
			return;
		}

	default:
		break;
	}
	mx_textual_class();
}

MX_BUTTON *mx__button(MX_BUTTON_DATA * button, size_t size, MX_OBJ_DATA * parent, int theid)
{
	MXMINSIZE(size, MX_BUTTON);

	button = (MX_BUTTON_DATA *) mx__textual(MXTEXTUAL(button), size, parent, theid);
	if (button) {
		MXOBJ(button)->_class = mx_button_class;
		MXDEBUG_ALLOCTAG(button);
		MXDEBUG_ATOMNAME(button, "button");

		mx_text_align(button, MX_ALIGN_CENTER);
		mx_wantpointer(button, true);
	}
	MXDEBUG_INVARIANT(MXOBJ(button));
	return (MX_BUTTON *) button;
}

void mx_winborder_class(void)
{
	MX_WINBORDER *border = (MX_WINBORDER *) mx.obj;
	MX_WIN *owner = MXOBJ(border)->_win;

	MXDEBUG_CLASSINVARIANT(MXOBJ(border));
	MXDEBUG_INVARIANT(MXOBJ(owner));

	switch (mx.event) {

	case MX_DESTRUCT:
		if (owner)
			owner->base.win.border = 0;
		break;

	case MX_EXPOSE:
		mx__theme->winborder(border);
		return;

	case MX_POINTER_PRESS:
		mx_dirty(border, true);
		if ((owner) && (!MXWIN(owner)->_nomove))
			mx_wantmove(border, true);
		return;

	case MX_POINTER_MOVE:
		if (mx_armed(border)) {
			const MX_POINTER_INFO *info = mx_pointer_info();

			if ((info->dx) || (info->dy)) {
				mx_dirty(border, true);
				mx_move(border, mx_x(border) + info->dx, mx_y(border) + info->dy);
				mx_dirty(border, true);

				if ((owner) && (!MXWIN(owner)->_nomove)) {
					mx_dirty(owner, true);
					mx_move(owner, mx_x(owner) + info->dx, mx_y(owner) + info->dy);
					mx_dirty(owner, true);
				}
			}
		}
		break;

	case MX_POINTER_RELEASE:
		mx_wantmove(border, false);
		return;

	case MX_THEME_CHANGE:
		mx_win_noclose(owner, mx_disabled(&border->_close));
		mx_win_noresize(owner, mx_disabled(&border->_resize));
		mx_defaultrect(border, 0);
		mx_geometry(border);
		return;

	case MX_GEOMETRY:
		mx_layout(&border->_close, (MX_LAYOUT_X2 | MX_LAYOUT_Y1), border, -1, 1);
		mx_layout(&border->_resize, (MX_LAYOUT_X2 | MX_LAYOUT_Y2), border, -1, -1);
		mx_geometry(&border->_close);
		mx_geometry(&border->_resize);
		return;

	case MX_DEFAULTRECT:
		if (owner) {
			int rh = 0, th = 0;
			MX_RECT temp;
			MX_RECT *rect = mx_defaultrect_data();
			const MX_RECT *ownerrect = MXRECT(owner);

			if (!mx_disabled(&border->_close)) {
				mx_defaultrect(&border->_close, &temp);
				th = temp.y2 - temp.y1 + 1;
			}
			if (!MXWIN(owner)->_notitle)
				th = MX_MAX(MXTEXTUAL(owner)->_textheight + 2, th);

			if (!mx_disabled(&border->_resize)) {
				mx_defaultrect(&border->_resize, &temp);
				rh = temp.y2 - temp.y1 + 1;
			}

			rect->x1 = ownerrect->x1 - 1;
			rect->y1 = ownerrect->y1 - th - 1;
			rect->x2 = ownerrect->x2 + 1;
			rect->y2 = ownerrect->y2 + rh + 1;
		}
		return;

	default:
		break;
	}
	mx_obj_class();
}

void mx_winborder_callback(MX_WIN * w)
{
	MX_WIN_DATA *win = MXWIN(w);
	MX_WINBORDER *border = win->border;

	MXDEBUG_INVARIANT(MXOBJ(win));
	MXDEBUG_INVARIANT(MXOBJ(border));
	MXDEBUG_INVARIANT(MXOBJ(&border->_close));
	MXDEBUG_INVARIANT(MXOBJ(&border->_resize));
	MXDEBUG_INVARIANT(MXOBJ(mx.obj));

	/* Resize window if necessary */
	if ((MXOBJ(mx.obj) == MXOBJ(&border->_resize))
		&& (mx.event == MX_POINTER_MOVE) && (mx_armed(&border->_resize))) {
		const MX_POINTER_INFO *info = mx_pointer_info();

		if ((info->dx) || (info->dy)) {
			if (win) {
				mx_win_dirty(w);

				mx_resize(win, mx_w(win) + info->dx, mx_h(win) + info->dy);
				mx_geometry(win);

				mx_win_dirty(w);

			} else {
				assert(0);

				mx_dirty(border, true);
				mx_resize(border, mx_w(border) + info->dx, mx_h(border) + info->dy);
				mx_geometry(border);
				mx_dirty(border, true);
			}
		}
	}

	/* Make sure the resize button doesnt get held down */
	if ((MXOBJ(mx.obj) == MXOBJ(&border->_resize))
		&& (mx.event == MX_SELECT) && (mx.data))
		mx_select(&border->_resize, false);

	/* Destroy window if close button is selected */
	if (mx_selected(&border->_close))
		mx_delete(win);
}

MX_WINBORDER *mx__winborder(MX_WINBORDER * border, size_t size, MX_WIN_DATA * owner, int theid)
{
	MXDEBUG_INVARIANT(MXOBJ(owner));
	MXDEBUG_INVARIANT(mx_tree_parent(MXOBJ(owner)));

	MXMINSIZE(size, MX_WINBORDER);

	border = (MX_WINBORDER *) mx__obj(MXOBJ(border), mx_winborder_class, size, mx_tree_parent(MXOBJ(owner)), theid);
	if (border) {
		border->_callback = mx_winborder_callback;
		MXOBJ(border)->_win = (MX_WIN *) owner;
		MXDEBUG_ALLOCTAG(border);
		MXDEBUG_ATOMNAME(border, "winborder");
		mx_wantpointer(border, true);

		mx_button(&border->_close, 0, border, MXID_WINCLOSE);
		MXDEBUG_ATOMNAME(&border->_close, "winborder_close");

		mx_button(&border->_resize, 0, border, MXID_WINRESIZE);
		MXDEBUG_ATOMNAME(&border->_resize, "winborder_resize");
		mx_wantmove(&border->_resize, true);

		mx_defaultrect(&border->_close, 0);
		mx_defaultrect(&border->_resize, 0);
	}
	MXDEBUG_INVARIANT(MXOBJ(border));
	return border;
}

void mx__win_dirty(MX_WIN_DATA * win)
{
	MXDEBUG_INVARIANT(MXOBJ(win));

	mx_dirty(win, true);

	if (win->border)
		mx_dirty(win->border, true);
}

static void mx__win_no(MX_WIN_DATA * win, MX_BUTTON * b, unsigned res)
{
	if ((win->border) && (b)) {
		MXDEBUG_INVARIANT(MXOBJ(win->border));
		MXDEBUG_INVARIANT(MXOBJ(b));

		if (res) {
			mx_disable(b, true);
			mx_resize(b, -1, -1);

		} else {
			mx_disable(b, false);
			mx_defaultrect(b, 0);
		}
	}
}

void mx__win_noresize(MX_WIN_DATA * win, unsigned res)
{
	MXDEBUG_INVARIANT(MXOBJ(win));

	if (win->border)
		mx__win_no(win, &win->border->_resize, res);
}

void mx__win_noclose(MX_WIN_DATA * win, unsigned res)
{
	MXDEBUG_INVARIANT(MXOBJ(win));

	if (win->border)
		mx__win_no(win, &win->border->_close, res);
}

void mx__win_notitle(MX_WIN_DATA * win, unsigned res)
{
	MXDEBUG_INVARIANT(MXOBJ(win));

	win->_notitle = res;
}

void mx__win_nomove(MX_WIN_DATA * win, unsigned res)
{
	MXDEBUG_INVARIANT(MXOBJ(win));

	win->_nomove = res;
}

static void mx__win_top(MX_WIN_DATA * win)
{
	MX_OBJ_DATA *parent;

	assert(win);

	if (!mx_tree_next(MXOBJ(win)))
		return;

	parent = mx_tree_parent(MXOBJ(win));
	MXDEBUG_INVARIANT(parent);
	if (!parent)
		return;

	if (win->border) {
		MXDEBUG_INVARIANT(MXOBJ(win->border));
		MX_TREE_REMOVE(MXOBJ(win->border));
		MX_TREE_INSERT(MXOBJ(win->border), parent);
	}

	MXDEBUG_INVARIANT(MXOBJ(win));
	MX_TREE_REMOVE(MXOBJ(win));
	MX_TREE_INSERT(MXOBJ(win), parent);
}

static void mx__activate(MX_WIN_DATA * win)
{
	MX_WIN_DATA *oldactive;

	if (win != mx__.popup)
		mx__popup(0);

	oldactive = mx__.active;
	mx__.active = win;
	if (oldactive)
		mx__win_dirty(oldactive);

	if ((win->_defaultfocus) && (win->_defaultfocus->_wantfocus)
		&& (!win->_defaultfocus->_disabled) && (!win->_focus))
		mx__focus(win->_defaultfocus);

	mx__win_top(win);
	mx__win_dirty(win);
}

void mx__popup(MX_WIN_DATA * win)
{
	if ((win == mx__.popup) && (win != MX__ROOTWIN))
		return;

	if (mx__.popup)
		mx_delete(mx__.popup);

	mx__.popup = win;
}

static void mx__root_geometry(const MX_RECT * r)
{
	assert(r);
	mx_place(&mx__.root, r);

	/*      mx_defaultrect(&mx__.rootclose, 0);
	   mx_move(&mx__.rootclose, mx_w(&mx__.root) - mx_w(&mx__.rootclose) - 1, 1);
	   mx_geometry(&mx__.rootclose); */

#if defined(MXDEBUG) && defined(MX_PLATFORM_DEGFX)
	mx__eventlogsize = mx_h(&mx__.root) / mx_font_height(0);
#endif
}

void mx_win_class(void)
{
	MX_WIN *w = (MX_WIN *) mx.obj;
	MX_WIN_DATA *win = MXWIN(w);

	MXDEBUG_CLASSINVARIANT(MXOBJ(w));

	switch (mx.event) {

	case MX_DESTRUCT:{
			MX_WIN_DATA *parent = mx_tree_parent(win);

			if ((win->_ismodal) && (parent)) {

				if (parent->_modallock)
					parent->_modallock -= 1;

				if (win == mx__.active)
					mx__activate(parent);
				else
					mx__win_dirty(parent);
			}

			if (win->border) {
				mx_delete(win->border);
				win->border = 0;
			}

			while (mx_tree_last(win)) {
				MX_WIN_DATA *ptr = mx_tree_last(win);

				mx__win_dirty(ptr);

				MX_TREE_REMOVE(ptr);
				mx_delete(ptr);
			}

			MX_TREE_REMOVE(win);
			break;
		}

	case MX_EXPOSE:
		mx__theme->win(w);
		return;

	case MX_GEOMETRY:
		if (win->border) {
			mx_defaultrect(win->border, 0);
			mx_geometry(win->border);
		}

		if (w == &mx__.root)
			mx__root_geometry(MXRECT(win));

		break;

	default:
		break;
	}
	mx_textual_class();
}

/**events !The default window handler */
void mx_default_handler(void)
{
	MX_WIN *w = MXOBJ(mx.obj)->_win;
	MX_WIN_DATA *win = MXWIN(w);

	MXDEBUG_INVARIANT(MXOBJ(win));
	MXDEBUG_INVARIANT(MX__CURRENT);

	/** If any object of a window is pressed, the window is brought to the top.  */
	if (mx.event == MX_POINTER_PRESS)
		mx__activate(win);

	/** This function passes the event to the desination object. */
	assert(MX__CURRENT->_class);
	MX__CURRENT->_class();

	if ((win->border) && (win->border->_callback))
		win->border->_callback((MX_WIN *) win);
}

/**events !The default window handler */
void mx_win_handler(MX_WIN * win)
{
	(void) win;

	MXDEBUG_INVARIANT(MXOBJ(mx.obj));
	MXDEBUG_INVARIANT(MXOBJ(win));
	assert(win == MXOBJ(mx.obj)->_win);

	mx_default_handler();
}

MX_WIN *mx__basicwin(MX_WIN_DATA * win, size_t size, MX_HANDLER handler, int theid)
{
	MXMINSIZE(size, MX_WIN);

	if (!handler)
		handler = mx_win_handler;

	win = (MX_WIN_DATA *) mx__textual(MXTEXTUAL(win), size, 0, theid);
	if (win) {
		MX_WIN_DATA *creatingwin = &MXOBJ(mx.obj)->_win->base.win;

		MXOBJ(win)->_class = mx_win_class;
		MXOBJ(win)->_win = (MX_WIN *) win;
		win->_handler = handler;
		MXDEBUG_ALLOCTAG(win);
		MXDEBUG_ATOMNAME(win, "win");

		/* Default is that a window is a child of the creating window */
		MXDEBUG_INVARIANT(MXOBJ(mx.obj));

		/* If an window seems to be 'creating itself' then its a window
		   created in the main function before event passing starts.  This
		   includes the root window. */
		if ((creatingwin) && (win != creatingwin)) {
			MXDEBUG_INVARIANT(MXOBJ(creatingwin));
			MX_TREE_INSERT(win, creatingwin);
		}
		mx_wantpointer(win, true);
	}

	MXDEBUG_INVARIANT(MXOBJ(win));
	return (MX_WIN *) win;
}

MX_WIN *mx__win(MX_WIN_DATA * win, size_t size, MX_HANDLER handler, int theid)
{
	win = (MX_WIN_DATA *) mx__basicwin(win, size, handler, theid);
	if (win) {
		win->border = mx__winborder(0, 0, win, MXID_WINBORDER);

		MX_TREE_REMOVE(MXOBJ(win));
		MX_TREE_INSERT(MXOBJ(win), MX__ROOTOBJ);
	}

	MXDEBUG_INVARIANT(MXOBJ(win));
	return (MX_WIN *) win;
}

void mx__win_modal(MX_WIN_DATA * win)
{
	MX_WIN_DATA *target = mx_tree_parent(win);

	MXDEBUG_INVARIANT(MXOBJ(win));
	MXDEBUG_INVARIANT(MXOBJ(target));

	if (mx__.active == target)
		mx__activate(win);

	win->_ismodal = true;
	++target->_modallock;

	mx__win_dirty(win);
	mx__win_dirty(target);
}

void mx__win_rootchild(MX_WIN_DATA * win)
{
	MX_WIN_DATA *p = mx_tree_parent(win);

	MXDEBUG_INVARIANT(MXOBJ(win));

	if ((p) && (p != MX__ROOTWIN)) {
		MX_TREE_REMOVE(win);
		MX_TREE_INSERT(win, MX__ROOTWIN);
	}
}

/**drawing_gui_objects
While handling an MX_EXPOSE event we can find out what area of the
screen is being exposed. */
const MX_RECT *mx_expose_rect(void)
{
	const MX_RECT *ret = (const MX_RECT *) mx.data;

	assert(mx.data);
	assert(mx.event == MX_EXPOSE);

	MXDEBUG_INVARIANT(MX__CURRENT);
	if (mx.event == MX_EXPOSE)
		return ret;

	return 0;
}

typedef struct MX__EXPOSEDATA {
	MX_OBJ_DATA *target;
	unsigned children;
} MX__EXPOSEDATA;

static MX__EXPOSEDATA mx__exposedata = { 0, false };

/**drawing_gui_objects
This function can only be used while handling an MX_EXPOSE event.  It
determines wether we are actually exposing the object or are just pretending
to expose so that we can find out what portions of the screen are used by the
object.  This function should probably not be used by end users, only by
theme designers.  */
unsigned mx_exposing(void)
{
	MXDEBUG_INVARIANT(MX__CURRENT);
	assert(mx.event == MX_EXPOSE);

	return ((mx.event == MX_EXPOSE)
			&& (mx__exposedata.target)) ? false : true;
}

static void mx__expose(MX_OBJ_DATA * obj, const MX_RECT * rect)
{
#if defined(MXDEBUG) && defined(MX_PLATFORM_DEGFX)
	int prev = 0;

	mx__stackcheck((const char *) &prev);
#endif

	if (!MXRECT_VALID(*rect))
		return;

	assert(mx__theme);
	MXDEBUG_INVARIANT(obj);
	MXDEBUG_INVARIANT(MXOBJ(obj->_win));

	/* A (little) dirty hack to make the mx.obj a derived class of
	   MX_OBJ_DATA which makes the nice macros usable even for mx.obj.  The
	   cast is safe becasue MX_OBJ is only a wrapper of MX_OBJ_DATA and has
	   (must have!) no data of its own.  Also again the ugly cast (see
	   mx__event) so users can see a const event stack. */
	((MX_EVENTSTACK *) (&mx))->obj = (MX_OBJ *) obj;
	((MX_EVENTSTACK *) (&mx))->event = MX_EXPOSE;
	((MX_EVENTSTACK *) (&mx))->data = rect;
	((MX_EVENTSTACK *) (&mx))->datalen = sizeof(*rect);
	((MX_EVENTSTACK *) (&mx))->_answer = 0;

	/* Are we just collecting dirty regions */
	if (mx__exposedata.target) {
		static MX_RECT dummy = { -1, -1, -2, -2 };

		/* Mark the region as dirty */
		if (mx__exposedata.target == obj)
			mx_platform_dirty(rect);

		/* Higher objects might have the target in the background so we must
		   allow the object to check background objects.  The clipping range
		   is set to nothing so that nothing will actually get drawn on the
		   screen. */
		mx__.clip = dummy;
		mx_platform_clip(&dummy);
		assert(MX__CURRENT);
		MX__CURRENT->_class();

		/* We are really drawing so set the clipping region and ask the
		   object to draw itself. */
	} else {
		mx__.clip = *rect;

		/**events,drawing_gui_objects MX_EXPOSE events dont get passed to
        the window handler.  Instead they go directly to the object class
        function. */
		if (mx_platform_clip(rect))
			MX__CURRENT->_class();
	}
}

static void mx__drawrecurse(MX_OBJ_DATA * obj, MX_OBJ_DATA * parent, const MX_RECT * rect)
{
	MXDEBUG_INVARIANT(parent);

	if (!MXRECT_VALID(*rect))
		return;

	while (obj) {
		MX_RECT subrect;

		MXDEBUG_INVARIANT(obj);

		if (MXRECTS_OVERLAP(*rect, *MXRECT(obj))) {
			MXRECT_INTERSECT(*rect, *MXRECT(obj), subrect);

			if ((mx__exposedata.target == obj)
				&& (mx__exposedata.children)) {
				mx__expose(obj, &subrect);
				return;

			} else
				mx__drawrecurse(mx_tree_last(obj), obj, &subrect);

			MX__TOP_RECTS(*rect, *MXRECT(obj), subrect);
			mx__drawrecurse(mx_tree_prev(obj), parent, &subrect);

			MX__LEFT_RECTS(*rect, *MXRECT(obj), subrect);
			mx__drawrecurse(mx_tree_prev(obj), parent, &subrect);

			MX__RIGHT_RECTS(*rect, *MXRECT(obj), subrect);
			mx__drawrecurse(mx_tree_prev(obj), parent, &subrect);

			MX__BOT_RECTS(*rect, *MXRECT(obj), subrect);
			mx__drawrecurse(mx_tree_prev(obj), parent, &subrect);
			return;
		}
		obj = mx_tree_prev(obj);
	}
	mx__expose(parent, rect);
}

void mx__gui_redraw(const MX_RECT * rect)
{
	MX__EXPOSEDATA old = mx__exposedata;
	const MX_EVENTSTACK prev = mx;

#ifdef MXDEBUG
	unsigned oldexposing = mx__.exposing;

	mx__.exposing = true;
#endif

	/* Redraws should only be called from the main gui loop */
	assert(MXOBJ(mx.obj) == MXOBJ(MX__ROOTWIN));
	assert(mx.event == MX_NOTHING);

	/* Check for a valid rectangle */
	assert(MXRECT_VALID(*rect));

	MXDEBUG_INVARIANT(MX__ROOTOBJ);
	mx__exposedata.target = 0;
	mx__exposedata.children = false;
	mx__drawrecurse(mx_tree_last(MX__ROOTOBJ), MX__ROOTOBJ, rect);
	mx__exposedata = old;

#ifdef MXDEBUG
	mx__.exposing = oldexposing;
#endif

	/* Again the ugly cast (see mx__event) so users can see a const event
	   stack. */
	memcpy((MX_EVENTSTACK *) & mx, &prev, sizeof(prev));
}

void mx__dirty(MX_OBJ_DATA * obj, const unsigned children)
{
	MX__EXPOSEDATA old = mx__exposedata;
	const MX_EVENTSTACK prev = mx;

#ifdef MXDEBUG
	unsigned oldexposing = mx__.exposing;

	mx__.exposing = true;
#endif

	MXDEBUG_INVARIANT(obj);
	MXDEBUG_INVARIANT(MX__ROOTOBJ);
	mx__exposedata.target = obj;
	mx__exposedata.children = children;
	mx__drawrecurse(mx_tree_last(MX__ROOTOBJ), MX__ROOTOBJ, MXRECT(obj));
	mx__exposedata = old;

#ifdef MXDEBUG
	mx__.exposing = oldexposing;
#endif

	/* Again the ugly cast (see mx__event) so users can a const event stack. */
	memcpy((MX_EVENTSTACK *) & mx, &prev, sizeof(prev));
}

/**drawing_gui_objects
This function can be used while handling an MX_EXPOSE event to tell
the drawing code to make sure that underlying objects are drawn first.  This
is necessary when drawing transparent or non-rectangular objects.  This function
is probably only useful for theme designers.*/
void mx_expose_background(const MX_RECT * rect)
{
	MX_RECT area;

	MXDEBUG_INVARIANT(MX__CURRENT);
	assert(mx.event == MX_EXPOSE);
	assert(MX__CURRENT != MX__ROOTOBJ);
#ifdef MXDEBUG
	assert(mx__.exposing);
#endif

	if (mx.event != MX_EXPOSE)
		return;

	if (!rect)
		rect = mx_expose_rect();

	MXRECT_INTERSECT(*mx_expose_rect(), *rect, area);

	if (!MXRECT_VALID(area))
		return;

	if (mx_tree_parent(MX__CURRENT)) {
		MX_RECT oldclip = mx__.clip;

		MXDEBUG_INVARIANT(mx_tree_parent(MX__CURRENT));

		mx__drawrecurse(mx_tree_prev(MX__CURRENT), mx_tree_parent(MX__CURRENT), &area);
		mx_platform_clip(&oldclip);

	} else {
		assert(0);
	}
}

static void mx__pointer_event(MX_EVENT event)
{
	MX_LOCK objlock, winlock;
	MX_OBJ_DATA *target = mx__.pointerhold ? mx__.pointerhold : mx__.pointed;

	if (!target)
		return;

	if ((target != MX__ROOTOBJ) && (mx__disabled(target)))
		return;

	if (event == MX_POINTER_PRESS)
		mx__.armed = target;

	else if ((event == MX_POINTER_MOVE) && (!target->_wantmove))
		return;

	mx_lock(&objlock, target);
	mx_lock(&winlock, target->_win);
	mx__event(target, event, 0, 0);

	if (event == MX_POINTER_PRESS) {
		mx__focus(target);

	} else if (event == MX_POINTER_RELEASE) {
		if (target == mx__.armed)
			mx__event(target, MX_POINTER_CLICK, 0, 0);
		mx__.armed = 0;
	}

	if (mx_lastlock(&objlock))
		mx__dirty(target, true);
	if (mx_lastlock(&winlock))
		mx_win_dirty(target->_win);
	mx_unlock(&objlock);
	mx_unlock(&winlock);
}

static MX_OBJ_DATA *mx__pointed(MX_OBJ_DATA * obj)
{
	MX_OBJ_DATA *child;

	MXDEBUG_INVARIANT(obj);

	if (!MXRECT_CONTAINS(*MXRECT(obj), mx__.pointer.x, mx__.pointer.y))
		return 0;

	child = mx_tree_last(obj);

	while (child) {
		if (MXRECT_CONTAINS(*MXRECT(child), mx__.pointer.x, mx__.pointer.y))
			return mx__pointed(child);

		child = mx_tree_prev(child);
	}
	return obj;
}

static MX_OBJ_DATA *mx__pointer_refuse(MX_OBJ_DATA * obj)
{
	MX_OBJ_DATA *ptr;

	if (!obj)
		return 0;

	MXDEBUG_INVARIANT(obj);

	if (obj->_wantpointer)
		return obj;

	ptr = mx_tree_prev(obj);
	while (ptr) {
		if (MXRECT_CONTAINS(*MXRECT(ptr), mx__.pointer.x, mx__.pointer.y))
			return mx__pointer_refuse(mx__pointed(ptr));

		ptr = mx_tree_prev(ptr);
	}

	return mx__pointer_refuse(mx_tree_parent(obj));
}

#if defined(MXDEBUG) && defined(MX_PLATFORM_DEGFX)
#   include "degfx/degfx.h"

static void mx__drawtree(MX_OBJ_DATA * obj, int *x, int *y)
{
	char temp[1024];

	sprintf(temp, "%s[%p]", MXDEBUG_ATOMNAME_GET(obj), (void *) obj);
	if (mx__armed(obj))
		strcat(temp, " Arm");
	if (mx__selected(obj))
		strcat(temp, " Select");
	if (obj == mx__.pointed)
		strcat(temp, " Pointed");
	if (obj == mx__.pointerhold)
		strcat(temp, " PointerHold");
	if ((mx__.active) && (obj == mx__.active->_focus))
		strcat(temp, " Focus");

	mx_font_draw(0, temp, -1, *x, *y, MXRGB(0, 0, 0));
	*x += 20;
	*y += mx_font_height(0);
	obj = mx_tree_last(obj);
	while (obj) {
		mx__drawtree(obj, x, y);
		obj = mx_tree_prev(obj);
	}
	*x -= 20;
}

static void mx__root_debugexpose(void)
{
	unsigned i;
	int x = mx_x1(&mx__.root);
	int y = mx_y1(&mx__.root);
	char temp[1024];

	sprintf(temp, "Frame rate %i", mx__.framerate);
	mx_font_draw(0, temp, -1, 0, y, MXRGB(0, 0, 0));
	y += mx_font_height(0);

	sprintf(temp, "Memory %i bytes %i allocs", mx__alloc_bytes, mx__alloc_allocs);
	mx_font_draw(0, temp, -1, 0, y, MXRGB(0, 0, 0));
	y += mx_font_height(0);

	sprintf(temp, "Max stack %i ", mx__stackmax - mx__stackmin);
	mx_font_draw(0, temp, -1, 0, y, MXRGB(0, 0, 0));
	y += mx_font_height(0);

	sprintf(temp, "Pointer %i %i %i", mx__.pointer.x, mx__.pointer.y, mx__.pointer.b);
	mx_font_draw(0, temp, -1, 0, y, MXRGB(0, 0, 0));
	y += mx_font_height(0);

	mx__drawtree(MX__ROOTOBJ, &x, &y);

	x = mx_w(&mx__.root) / 2;
	y = mx_y1(&mx__.root);

	for (i = 0; i < mx_vector_size(&mx__eventlog); i++) {
		int len = -1;
		const char *t = mx_string_text(&mx__eventlog.data[i], &len);

		mx_font_draw(0, t, len, x, y, MXRGB(0, 0, 0));
		y += mx_font_height(0);
	}
}
#endif

static void mx__root_class(void)
{
	MXDEBUG_CLASSINVARIANT(MXOBJ(mx.obj));

	switch (mx.event) {

	case MX_EXPOSE:
		mx__theme->root(&mx__.root);
#if defined(MXDEBUG) && defined(MX_PLATFORM_DEGFX)
		mx__root_debugexpose();
#endif
		break;

	default:
		mx_win_class();
		break;
	}
}

/** !The initialization function for DEPUI. */
unsigned mx_start(void)
{
#if defined(MXDEBUG) && defined(MX_PLATFORM_DEGFX)
	const char temp = ' ';

	mx__stackcheck((const char *) &temp);
#endif

				/** If DEPUI has already been started then this function does nothing. */
	if (MXCLASS(&mx__.root) != mx__root_class) {
		int i = 0;
		const MX_PLATFORM_RESOLUTION *r = 0;
		const MX_PLATFORM_DEPTH *c = 0;
		const MX_PLATFORM_DRIVER *d = 0;
		const MX_PLATFORM_THEME *t = 0;
		const MX_PLATFORM_FONT *f;

#ifdef MXDEBUG
		fprintf(stderr, "Init depui root.\n");
#endif
		memset(&mx__, 0, sizeof(mx__));
		mx__theme = 0;

		if (!mx_platform_start(0, 0, 0, 0))
			return false;

		mx_platform_modes(&d, &r, &c, &t);

								/** The default theme is started. */
		do {
			mx__theme = &t->theme[i++];
		}
		while ((mx__theme) && (!mx__theme->start()));
		if (!mx__theme)
			return false;

								/** The default and built-in fonts are loaded. */
		i = 0;
		f = mx_platform_font_system(i);
		while (f) {
			MX_GUIFONT *guifont = mx__guifont(f->font, f->text, false);

			mx__guifont_entry(guifont, &mx__.builtinfonts);
			mx_delete(guifont);

			f = mx_platform_font_system(++i);
		}

		f = mx_platform_font_system(-1);
		if (f) {
			MX_GUIFONT *guifont = mx__guifont(f->font, "platform default", false);
			if (guifont) {
				mx_ref(&mx__.defaultfont, guifont);
				mx_delete(guifont);
			}
		}

		/* Setup a first level event stack.  We use the ugly cast again (see
		   mx_obj_event) so that users can see a const event stack. */
		((MX_EVENTSTACK *) (&mx))->obj = (MX_OBJ *) MX__ROOTOBJ;
		((MX_EVENTSTACK *) (&mx))->event = MX_NOTHING;

		/** The root window is created. */
		mx_basicwin(&mx__.root, 0, 0, 0);
		MX__ROOTOBJ->_class = mx__root_class;
		MX__ROOTOBJ->_win = &mx__.root;
		MXDEBUG_ATOMNAME(&mx__.root, "root");
		mx_wantpointer(&mx__.root, true);

		mx__root_geometry(mx_platform_rect());
		mx_platform_dirty(mx_platform_rect());

#ifdef MXDEBUG
		mx__.framenext = clock() + CLOCKS_PER_SEC;
#endif
	}
				/** This function returns non-zero if initialization succeeded. */
	return true;
}

/** !The main gui loop for DEPUI */
int mx_execute(void)
{
				/** If the gui has not yet been initialized with mx_start() this function
    calls mx_start() now. */
	if (!mx_start())
		return EXIT_FAILURE;

				/** If this function is being called recursively it returns immediately.
    This possibility makes the source code for a simple main gui function and
    launching a gui window to be exactly the same. */
	if (mx__.running) {
#ifdef MXDEBUG
		fprintf(stderr, "Skipping mainloop.\n");
#endif
		return EXIT_SUCCESS;
	}
#ifdef MXDEBUG
	fprintf(stderr, "Starting mainloop.\n");
#endif

		/** The first time this function is called it loops until the gui is closed. */
	mx__.running = true;
	while (mx__.running) {
		int px, py, pb;
		MX_KEY_INFO key;
		const MX_RECT *rootrect;
		MX_RECT gfxrect;
		MX_LOCK rootlock;

		rootrect = MXRECT(&mx__.root);
		gfxrect = *mx_platform_rect();

		MXDEBUG_INVARIANT(MX__ROOTOBJ);
		MXDEBUG_ALLOCCHECK();

		mx_lock(&rootlock, &mx__.root);

				/** If the screen dimensions change between interation of the main gui
        loop then the root object is adjusted to fill the entire screen and
        the entire screen is marked for redrawing. */
		if ((gfxrect.x1 != rootrect->x1) || (gfxrect.y1 != rootrect->y1)
			|| (gfxrect.x2 != rootrect->x2)
			|| (gfxrect.y2 != rootrect->y2)) {
			mx__root_geometry(&gfxrect);
			mx_platform_dirty(&gfxrect);
		}

		if (!mx_platform_poll())
			mx_delete(&mx__.root);

				/** The guimain loop will stop if there are no child objects. */
		if (!mx_tree_last(MX__ROOTOBJ))
			mx_delete(&mx__.root);

		if (mx_platform_pointer(&px, &py, &pb)) {
			MX_OBJ_DATA *pointed = mx__.pointed;
			const unsigned moved = ((px != mx__.pointer.x)
									|| (py != mx__.pointer.y)) ? true : false;
			const unsigned pressed = ((pb)
									  && (!mx__.pointer.b)) ? true : false;
			const unsigned release = ((!pb)
									  && (mx__.pointer.b)) ? true : false;

			if ((moved) || (pressed) || (release)) {
				mx__.pointer.dx = px - mx__.pointer.x;
				mx__.pointer.dy = py - mx__.pointer.y;
				mx__.pointer.x = px;
				mx__.pointer.y = py;
				mx__.pointer.b = pb;
			}

						/** If the pointer is moved over an object and that object has
            called mx_obj_wantmove(true) then the object will recieve a
            MX_POINTER_MOVE event whenever the pointer moves over the object.
            See the function mx_obj_wantmove(). */
			if (moved)
				mx__pointer_event(MX_POINTER_MOVE);

			if ((!pointed) || (moved)) {
				pointed = mx__pointed(MX__ROOTOBJ);

						/** If an object calls mx_pointer_hold() the the object
                will continue recieving pointer messages even after the pointer
                actually leaves the area of the object on screen.  See the function
                mx_pointer_hold(). */
				if ((pointed) && (pointed != mx__.pointed)
					&& (!mx__.pointerhold))
					pointed = mx__pointer_refuse(pointed);
			}

												/** If the pointer enters or leaves the area of the object on screen
            then the appropriate object recieve MX_POINTER_LEAVE and MX_POINTER_ENTER
            events.  These event do not occur if an object has called mx_pointer_hold(). */
			if ((pointed != mx__.pointed) && (!mx__.pointerhold)) {
				mx__pointer_event(MX_POINTER_LEAVE);

				mx__.armed = 0;
				mx__.pointed = pointed;
				mx__pointer_event(MX_POINTER_ENTER);
			}

			/** If the pointer is pressed or released then the approriate object
            recieves MX_POINTER_PRESS or MX_POINTER_RELEASE events. */
			if (pressed)
				mx__pointer_event(MX_POINTER_PRESS);

			if (release)
				mx__pointer_event(MX_POINTER_RELEASE);
		}

		if (mx_platform_key(&key.code, &key.ascii)) {

						/** If a key is pressed the focus object gets a MX_KEY event.  If the
            focus object uses the key then it should return non-zero from the event
            handler (see function mx_answer()).  If event handler return zero then
            the objects window will recieve a MX_KEY_UNUSED event. */
			if (mx__.active) {
				unsigned int ret = 0;

				if (mx__.active->_focus)
					ret = mx__event(mx__.active->_focus, MX_KEY, &key, sizeof(key));

				if ((!ret) && (mx__.active))
					mx_event(mx__.active, MX_KEY_UNUSED, &key, sizeof(key));
			}
		}
#ifdef MX_PLATFORM_DEGFX
#ifdef MXDEBUG
		mx__dirty(MX__ROOTOBJ, false);
#endif
		++mx__.framecount;

		if (clock() > mx__.framenext) {
			mx__.framenext = clock() + CLOCKS_PER_SEC;

			mx__.framerate = mx__.framecount;
			mx__.framecount = 0;
		}
#endif
		if (mx_unlock(&rootlock))
			mx__.running = false;
	}
#ifdef MXDEBUG
	fprintf(stderr, "Exited main loop.\n");
#endif

	/* Release loaded fonts */
	while (mx_dllist_last(&mx__.loadedfonts)) {
		MX__GUIFONT_ELEM *entry = mx_dllist_last(&mx__.loadedfonts);

		mx_unref(&entry->ref);
		MX_DLLIST_REMOVE(entry);
		mx_free(entry);
	}

	/* Release builtin fonts */
	while (mx_dllist_last(&mx__.builtinfonts)) {
		MX__GUIFONT_ELEM *entry = mx_dllist_last(&mx__.builtinfonts);

		mx_unref(&entry->ref);
		MX_DLLIST_REMOVE(entry);
		mx_free(entry);
	}

	/* Release default font */
	mx_unref(&mx__.defaultfont);

	/* Stop the platforms */
	mx__theme->stop();
	mx_platform_stop();

#if defined(MXDEBUG) && defined(MX_PLATFORM_DEGFX)
	{
		unsigned int i;

		for (i = 0; i < mx_vector_size(&mx__eventlog); i++)
			mx_string_free(&mx__eventlog.data[i]);

		mx_vector_free(&mx__eventlog);
	}
#endif

	/* Cleanup really well */
	memset(&mx__, 0, sizeof(mx__));
	mx__theme = 0;

#ifdef MXDEBUG
	fprintf(stderr, "Cleanup complete.\n");
#endif

	return EXIT_SUCCESS;
}

void mx__puiexit(void)
{
	mx_delete(&mx__.root);
}
